/*    
 * Copyright (c) 2013 ISoftStone, Inc.
 * All right reserved.
 *
 * 文件名：      EmailSupportServiceImpl.java
 * 作者:     Jacky Wang
 * 创建日期： 2013-8-6 上午10:55:02
 * 版本：           
 *
 */
package com.isoftstone.industry.aaa.core.service.email.impl;

import java.io.File;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import net.sf.json.JSONArray;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import com.isoftstone.industry.persistance.bean.AccountItem;
import com.isoftstone.industry.persistance.bean.Bill;
import com.isoftstone.industry.persistance.bean.BillItem;
import com.isoftstone.industry.persistance.bean.BillItemAcctItem;
import com.isoftstone.industry.persistance.bean.EmailNotification;
import com.isoftstone.industry.persistance.bean.SysMailTemplate;
import com.isoftstone.industry.persistance.bean.User;
import com.isoftstone.industry.aaa.constants.Constants;
import com.isoftstone.industry.aaa.core.dao.assist.EmailNotificationDao;
import com.isoftstone.industry.aaa.core.service.assist.IBillService;
import com.isoftstone.industry.aaa.core.service.assist.ISysMailTemplateService;
import com.isoftstone.industry.aaa.core.service.email.IEmailSupportService;
import com.isoftstone.industry.aaa.core.service.email.entity.AcctItemTpl;
import com.isoftstone.industry.aaa.core.service.email.entity.BillItemDetailTpl;
import com.isoftstone.industry.aaa.core.service.email.entity.BillItemTpl;
import com.isoftstone.industry.aaa.core.service.email.entity.BillTpl;
import com.isoftstone.industry.aaa.utils.MailProperties;
import com.isoftstone.industry.aaa.utils.SystemProperties;
import com.isoftstone.modules.core.utils.DateUtils;
import com.isoftstone.modules.core.utils.json.JsonHelper;
import com.isoftstone.modules.core.utils.lock.FileProgrameLock;
import com.isoftstone.modules.core.utils.lock.Lock;
import com.isoftstone.modules.core.utils.mail.Email;
import com.isoftstone.modules.core.utils.mail.MailUtils;

import freemarker.template.Template;

/**
 * @author jacky
 */
@Service
public class EmailSupportServiceImpl implements IEmailSupportService
{
	private Logger					logger	= Logger.getLogger(EmailSupportServiceImpl.class);

	@Resource
	private ISysMailTemplateService	sysMailTemplateService;

	@Resource(name = "mailSender")
	private JavaMailSender			mailSender;

	@Resource(name = "freeMarker")
	private FreeMarkerConfigurer	freeMarkerCfg;

	@Resource
	private IBillService			billService;

	@Resource
	private EmailNotificationDao	emailNotificationDao;
	@Resource
	private SystemProperties systemProperties;

	private BillTpl createBillTpl(User user, Bill bill, Timestamp now)
	{
		BillTpl result = new BillTpl();
		result.setUserName(user.getUsername());
		result.setCreatedTime(DateUtils.formatTimestamp(bill.getCreatedTime()));
		result.setDeliverTime(DateUtils.formatTimestamp(now));
		result.setStartDate(DateUtils.date2String(bill.getBillingCycle().getBeginDate()));
		result.setEndDate(DateUtils.date2String(bill.getBillingCycle().getEndDate()));
		result.setAmount(bill.getAmount());
		result.setPaidAmount(bill.getPaidAmount());
		result.setPayableAmount(bill.getPayableAmount());
		for (BillItem billItem : bill.getBillItems())
		{
			BillItemTpl bItemTpl = new BillItemTpl();
			bItemTpl.setPrintOrder(billItem.getPrintOrder());
			bItemTpl.setServName(billItem.getProdInst().getProduct().getName());
			double sumAmount = 0.00;
			for (BillItemAcctItem item : billItem.getBillAcctItems())
			{
				AccountItem acctItem = item.getAccountItem();
				AcctItemTpl acctItemTpl = new AcctItemTpl();
				acctItemTpl.setTypeName(acctItem.getAccountItemType().getName());
				acctItemTpl.setTypeId(acctItem.getAccountItemType().getId());
				acctItemTpl.setAmount(acctItem.getAmount());
				sumAmount += acctItemTpl.getAmount();
				bItemTpl.addAcctItem(acctItemTpl);
			}
			bItemTpl.setSumAmount(sumAmount);
			List<BillItemDetailTpl> details = convert2BillItemDetails(bItemTpl.getAcctItems());
			bItemTpl.setDetails(details);
			result.addBillItem(bItemTpl);
		}
		return result;
	}

	private List<BillItemDetailTpl> convert2BillItemDetails(
			List<AcctItemTpl> acctItems) {
		List<BillItemDetailTpl> details = new ArrayList<BillItemDetailTpl>();
		for (AcctItemTpl acctItemDto : acctItems) {
			BillItemDetailTpl detail = BillItemDetailTpl.getById(acctItemDto
					.getTypeId(), details);
			if (detail != null) {
				detail.setName(acctItemDto.getTypeName());
				detail.setAmount(detail.getAmount() + acctItemDto.getAmount());
			} else {
				detail = new BillItemDetailTpl();
				detail.setName(acctItemDto.getTypeName());
				detail.setAmount(acctItemDto.getAmount());
				detail.setId(acctItemDto.getTypeId());
				details.add(detail);
			}
		}

		return details;
	}

	@Override
	public void notifyAccountFreeze(String userId)
	{
		saveEmailNotification(userId, EmailNotification.Type.ACCOUNT_FREEZE, null);

	}

	@Override
	public void notifyProductInstDisabled(String userId, Set<String> snList)
	{
		saveEmailNotification(userId, EmailNotification.Type.PRODUCT_INSTANCE_DISABLED, JsonHelper.toJSONString(snList));
	}

	@Override
	public void notifySendBill(String userId, String billId)
	{
		saveEmailNotification(userId, EmailNotification.Type.SEND_BILL, billId);
	}

	@Override
	public boolean existInToday(String userId, String type)
	{
		Timestamp start = DateUtils.createStartTime(DateUtils.today());
		int count = emailNotificationDao.countByUserAndTypeAndCreatedTime(userId, type, start);
		return count > 0;
	}

	@Override
	@Transactional
	public void saveEmailNotification(String userId, String type, String extra)
	{
		EmailNotification entity = new EmailNotification();
		entity.setType(type);
		User user = new User();
		user.setId(userId);
		entity.setUser(user);
		entity.setExtra(extra);
		entity.setCreatedTime(DateUtils.getCurrentTimestamp());
		entity.setDelivered(false);
		emailNotificationDao.save(entity);
	}

	@Override
	@Transactional
	public void timingHandle()
	{
		//找出未发送的邮件通知，分页处理
		long total = emailNotificationDao.countByDelivered(false);
		int limit = 50;
		int start = 0;
		int pageNum = 0;
		while (start < total)
		{
			List<EmailNotification> page = emailNotificationDao.getNeedDeliveredNotification(start, limit);
			for (EmailNotification data : page)
			{
				Lock lock = getFileLock(data);
				//若可以成功获取文件锁，则代表没有进程正在为该用户进行出帐
				if (lock.obtain() == true)
				{
					try{
						sendEmail(data);
					}catch (Exception e){
						logger.error(e.getMessage(), e);
					}finally{
						//释放文件锁
						lock.unlock();
					}
				}
			}
			pageNum++;
			start += pageNum * limit;
		}
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void sendEmail(EmailNotification data)
	{
		String type = data.getType();
		boolean result = false;
		if (EmailNotification.Type.SEND_BILL.equals(type))
		{
			result = sendBillEmail(data);
		} else if (EmailNotification.Type.OWNFEE_USER_ASK_FOR_PAYMENT.equals(type))
		{
			result = sendAsk4PaymentEmail(data);
		} else if (EmailNotification.Type.ACCOUNT_FREEZE.equals(type))
		{
			result = sendAccountFreezeEmail(data);
		} else if (EmailNotification.Type.PRODUCT_INSTANCE_DISABLED.equals(type))
		{
			result = sendProdInstDisabledEmail(data);
		}

		if (result == true)
		{
			emailNotificationDao.markDelivered(data.getId(), true, DateUtils.getCurrentTimestamp());
		}
	}

	private boolean sendProdInstDisabledEmail(EmailNotification data)
	{
		User user = data.getUser();

		SysMailTemplate mailTpl = sysMailTemplateService.findBySn(Constants.MAIL_TEMPLATE_PROD_INST_DISABLE_SN);
		Map<String, Object> map = new HashMap<String, Object>();
		Email email = new Email();
		email.setAddress(user.getCustomer().getEmail());
		email.setSubject(mailTpl.getSubject());
		email.setSender(MailProperties.getMailFrom());
		map.put("email", email);
		map.put("htmlTemplate", mailTpl.getContent());
		map.put("userName", user.getUsername());
		String extra = data.getExtra();
		if (StringUtils.isNotEmpty(extra))
		{
			JSONArray array = JSONArray.fromObject(extra);
			StringBuffer strBuff = new StringBuffer();
			int size = array.size();
			for (int i = 0; i < size; i++)
			{
				strBuff.append(array.getString(i));
				if (i != size - 1)
				{
					strBuff.append(",");
				}
			}
			map.put("sn", strBuff.toString());
		}
		MailUtils.sendMailByAsynchronousMode(map, mailSender);
		return true;
	}

	private boolean sendAccountFreezeEmail(EmailNotification data)
	{
		User user = data.getUser();

		SysMailTemplate mailTpl = sysMailTemplateService.findBySn(Constants.MAIL_TEMPLATE_ACCOUNT_FREEZE_SN);
		Map<String, Object> map = new HashMap<String, Object>();
		Email email = new Email();
		email.setAddress(user.getCustomer().getEmail());
		email.setSubject(mailTpl.getSubject());
		email.setSender(MailProperties.getMailFrom());
		map.put("email", email);
		map.put("htmlTemplate", mailTpl.getContent());
		map.put("userName", user.getUsername());
		MailUtils.sendMailByAsynchronousMode(map, mailSender);
		return true;
	}

	private boolean sendAsk4PaymentEmail(EmailNotification data)
	{
		User user = data.getUser();

		SysMailTemplate mailTpl = sysMailTemplateService.findBySn(Constants.MAIL_TEMPLATE_ASK_FOR_PAYMENT_SN);
		Map<String, Object> map = new HashMap<String, Object>();
		Email email = new Email();
		email.setAddress(user.getCustomer().getEmail());
		email.setSubject(mailTpl.getSubject());
		email.setSender(MailProperties.getMailFrom());
		map.put("email", email);
		map.put("htmlTemplate", mailTpl.getContent());
		map.put("userName", user.getUsername());
		MailUtils.sendMailByAsynchronousMode(map, mailSender);
		return true;
	}

	private boolean sendBillEmail(EmailNotification data)
	{
		String billId = data.getExtra();
		User user = data.getUser();
		Bill bill = billService.findOne(billId);

		SysMailTemplate mailTpl = sysMailTemplateService.findBySn(Constants.MAIL_TEMPLATE_BILL_SN);
		Map<String, Object> map = new HashMap<String, Object>();
		Email email = new Email();
		email.setAddress(user.getCustomer().getEmail());
		email.setSubject(mailTpl.getSubject());
		email.setSender(MailProperties.getMailFrom());
		map.put("email", email);
		map.put("htmlTemplate", mailTpl.getContent());
		map.put("userName", user.getUsername());
		try
		{
			Timestamp now = DateUtils.getCurrentTimestamp();
			Template template = freeMarkerCfg.getConfiguration().getTemplate("bill.tpl");
			BillTpl billTpl = createBillTpl(user, bill, now);
			String htmlText = FreeMarkerTemplateUtils.processTemplateIntoString(template, billTpl);
			map.put("content", htmlText);
			MailUtils.sendMailByAsynchronousMode(map, mailSender);
			billService.markMailDelivered(bill.getId(), now);
		} catch (Exception e)
		{
			logger.error("邮件发送账单出错", e);
			return false;
		}
		return true;
	}

	private Lock getFileLock(EmailNotification notification)
	{
		String fileName = systemProperties.getFileLockDir() + File.separator + "emailsender_" + notification.getId() + ".lock";
		return FileProgrameLock.get(fileName);
	}
}
